/*
 * Main.cpp
 *
 *  Created on: 29.03.2013
 *      Author: sschweigert
 */

#include <iostream>
#include <objLoader.h>

#include <Rasterizer.h>
#include <ScanLineRasterizer.h>
#include <ScanLineRasterizerMT.h>

#include <highgui.h>

void SimpleVertexShader(VertexShaderParameters* param)
{
	param->out->p = param->globals->worldViewProj * param->in->p;
	param->out->varyings[0] = param->in->t.x;
	param->out->varyings[1] = param->in->t.y;
}

void SimpleFragmentShader(FragmentShaderParameters* p)
{
	p->out = p->material->texture->getBilinear(p->fragment->varyings[0], p->fragment->varyings[1]);
	//p->out = Color4(1,0,1,1);
}




void buildMesh(IRasterizer* r)
{
	Vec2 TexCoords[78];
	Vec4 Colors[42];
	Vec4 Normals[42];
	Vec4 Vertices[42];

	int tc = 0, c = 0, n = 0, v = 0;

	TexCoords[tc++] = Vec2(0.0, 0.0); Colors[c++] = Vec4(1.0, 1.0, 1.0); Normals[n++] = Vec4(-1.0, 0.0, 0.0); Vertices[v++] = Vec4(-0.5,-0.5,-0.5);
	TexCoords[tc++] = Vec2(1.0, 0.0); Colors[c++] = Vec4(1.0, 1.0, 0.0); Normals[n++] = Vec4(-1.0, 0.0, 0.0); Vertices[v++] = Vec4(-0.5,-0.5, 0.5);
	TexCoords[tc++] = Vec2(1.0, 1.0); Colors[c++] = Vec4(1.0, 0.0, 0.0); Normals[n++] = Vec4(-1.0, 0.0, 0.0); Vertices[v++] = Vec4(-0.5, 0.5, 0.5);
	TexCoords[tc++] = Vec2(1.0, 1.0); Colors[c++] = Vec4(1.0, 0.0, 0.0); Normals[n++] = Vec4(-1.0, 0.0, 0.0); Vertices[v++] = Vec4(-0.5, 0.5, 0.5);
	TexCoords[tc++] = Vec2(0.0, 1.0); Colors[c++] = Vec4(1.0, 0.0, 1.0); Normals[n++] = Vec4(-1.0, 0.0, 0.0); Vertices[v++] = Vec4(-0.5, 0.5,-0.5);
	TexCoords[tc++] = Vec2(0.0, 0.0); Colors[c++] = Vec4(1.0, 1.0, 1.0); Normals[n++] = Vec4(-1.0, 0.0, 0.0); Vertices[v++] = Vec4(-0.5,-0.5,-0.5);

	TexCoords[tc++] = Vec2(0.0, 0.0); Colors[c++] = Vec4(0.0, 1.0, 0.0); Normals[n++] = Vec4( 1.0, 0.0, 0.0); Vertices[v++] = Vec4( 0.5,-0.5, 0.5);
	TexCoords[tc++] = Vec2(1.0, 0.0); Colors[c++] = Vec4(0.0, 1.0, 1.0); Normals[n++] = Vec4( 1.0, 0.0, 0.0); Vertices[v++] = Vec4( 0.5,-0.5,-0.5);
	TexCoords[tc++] = Vec2(1.0, 1.0); Colors[c++] = Vec4(0.0, 0.0, 1.0); Normals[n++] = Vec4( 1.0, 0.0, 0.0); Vertices[v++] = Vec4( 0.5, 0.5,-0.5);
	TexCoords[tc++] = Vec2(1.0, 1.0); Colors[c++] = Vec4(0.0, 0.0, 1.0); Normals[n++] = Vec4( 1.0, 0.0, 0.0); Vertices[v++] = Vec4( 0.5, 0.5,-0.5);
	TexCoords[tc++] = Vec2(0.0, 1.0); Colors[c++] = Vec4(0.0, 0.0, 0.0); Normals[n++] = Vec4( 1.0, 0.0, 0.0); Vertices[v++] = Vec4( 0.5, 0.5, 0.5);
	TexCoords[tc++] = Vec2(0.0, 0.0); Colors[c++] = Vec4(0.0, 1.0, 0.0); Normals[n++] = Vec4( 1.0, 0.0, 0.0); Vertices[v++] = Vec4( 0.5,-0.5, 0.5);

	TexCoords[tc++] = Vec2(0.0, 0.0); Colors[c++] = Vec4(1.0, 1.0, 1.0); Normals[n++] = Vec4( 0.0,-1.0, 0.0); Vertices[v++] = Vec4(-0.5,-0.5,-0.5);
	TexCoords[tc++] = Vec2(1.0, 0.0); Colors[c++] = Vec4(0.0, 1.0, 1.0); Normals[n++] = Vec4( 0.0,-1.0, 0.0); Vertices[v++] = Vec4( 0.5,-0.5,-0.5);
	TexCoords[tc++] = Vec2(1.0, 1.0); Colors[c++] = Vec4(0.0, 1.0, 0.0); Normals[n++] = Vec4( 0.0,-1.0, 0.0); Vertices[v++] = Vec4( 0.5,-0.5, 0.5);
	TexCoords[tc++] = Vec2(1.0, 1.0); Colors[c++] = Vec4(0.0, 1.0, 0.0); Normals[n++] = Vec4( 0.0,-1.0, 0.0); Vertices[v++] = Vec4( 0.5,-0.5, 0.5);
	TexCoords[tc++] = Vec2(0.0, 1.0); Colors[c++] = Vec4(1.0, 1.0, 0.0); Normals[n++] = Vec4( 0.0,-1.0, 0.0); Vertices[v++] = Vec4(-0.5,-0.5, 0.5);
	TexCoords[tc++] = Vec2(0.0, 0.0); Colors[c++] = Vec4(1.0, 1.0, 1.0); Normals[n++] = Vec4( 0.0,-1.0, 0.0); Vertices[v++] = Vec4(-0.5,-0.5,-0.5);

	TexCoords[tc++] = Vec2(0.0, 0.0); Colors[c++] = Vec4(1.0, 0.0, 0.0); Normals[n++] = Vec4( 0.0, 1.0, 0.0); Vertices[v++] = Vec4(-0.5, 0.5, 0.5);
	TexCoords[tc++] = Vec2(1.0, 0.0); Colors[c++] = Vec4(0.0, 0.0, 0.0); Normals[n++] = Vec4( 0.0, 1.0, 0.0); Vertices[v++] = Vec4( 0.5, 0.5, 0.5);
	TexCoords[tc++] = Vec2(1.0, 1.0); Colors[c++] = Vec4(0.0, 0.0, 1.0); Normals[n++] = Vec4( 0.0, 1.0, 0.0); Vertices[v++] = Vec4( 0.5, 0.5,-0.5);
	TexCoords[tc++] = Vec2(1.0, 1.0); Colors[c++] = Vec4(0.0, 0.0, 1.0); Normals[n++] = Vec4( 0.0, 1.0, 0.0); Vertices[v++] = Vec4( 0.5, 0.5,-0.5);
	TexCoords[tc++] = Vec2(0.0, 1.0); Colors[c++] = Vec4(1.0, 0.0, 1.0); Normals[n++] = Vec4( 0.0, 1.0, 0.0); Vertices[v++] = Vec4(-0.5, 0.5,-0.5);
	TexCoords[tc++] = Vec2(0.0, 0.0); Colors[c++] = Vec4(1.0, 0.0, 0.0); Normals[n++] = Vec4( 0.0, 1.0, 0.0); Vertices[v++] = Vec4(-0.5, 0.5, 0.5);

	TexCoords[tc++] = Vec2(0.0, 0.0); Colors[c++] = Vec4(0.0, 1.0, 1.0); Normals[n++] = Vec4( 0.0, 0.0,-1.0); Vertices[v++] = Vec4( 0.5,-0.5,-0.5);
	TexCoords[tc++] = Vec2(1.0, 0.0); Colors[c++] = Vec4(1.0, 1.0, 1.0); Normals[n++] = Vec4( 0.0, 0.0,-1.0); Vertices[v++] = Vec4(-0.5,-0.5,-0.5);
	TexCoords[tc++] = Vec2(1.0, 1.0); Colors[c++] = Vec4(1.0, 0.0, 1.0); Normals[n++] = Vec4( 0.0, 0.0,-1.0); Vertices[v++] = Vec4(-0.5, 0.5,-0.5);
	TexCoords[tc++] = Vec2(1.0, 1.0); Colors[c++] = Vec4(1.0, 0.0, 1.0); Normals[n++] = Vec4( 0.0, 0.0,-1.0); Vertices[v++] = Vec4(-0.5, 0.5,-0.5);
	TexCoords[tc++] = Vec2(0.0, 1.0); Colors[c++] = Vec4(0.0, 0.0, 1.0); Normals[n++] = Vec4( 0.0, 0.0,-1.0); Vertices[v++] = Vec4( 0.5, 0.5,-0.5);
	TexCoords[tc++] = Vec2(0.0, 0.0); Colors[c++] = Vec4(0.0, 1.0, 1.0); Normals[n++] = Vec4( 0.0, 0.0,-1.0); Vertices[v++] = Vec4( 0.5,-0.5,-0.5);

	TexCoords[tc++] = Vec2(0.0, 0.0); Colors[c++] = Vec4(1.0, 1.0, 0.0); Normals[n++] = Vec4( 0.0, 0.0, 1.0); Vertices[v++] = Vec4(-0.5,-0.5, 0.5);
	TexCoords[tc++] = Vec2(1.0, 0.0); Colors[c++] = Vec4(0.0, 1.0, 0.0); Normals[n++] = Vec4( 0.0, 0.0, 1.0); Vertices[v++] = Vec4( 0.5,-0.5, 0.5);
	TexCoords[tc++] = Vec2(1.0, 1.0); Colors[c++] = Vec4(0.0, 0.0, 0.0); Normals[n++] = Vec4( 0.0, 0.0, 1.0); Vertices[v++] = Vec4( 0.5, 0.5, 0.5);
	TexCoords[tc++] = Vec2(1.0, 1.0); Colors[c++] = Vec4(0.0, 0.0, 0.0); Normals[n++] = Vec4( 0.0, 0.0, 1.0); Vertices[v++] = Vec4( 0.5, 0.5, 0.5);
	TexCoords[tc++] = Vec2(0.0, 1.0); Colors[c++] = Vec4(1.0, 0.0, 0.0); Normals[n++] = Vec4( 0.0, 0.0, 1.0); Vertices[v++] = Vec4(-0.5, 0.5, 0.5);
	TexCoords[tc++] = Vec2(0.0, 0.0); Colors[c++] = Vec4(1.0, 1.0, 0.0); Normals[n++] = Vec4( 0.0, 0.0, 1.0); Vertices[v++] = Vec4(-0.5,-0.5, 0.5);

	TexCoords[tc++] = Vec2(-0.75,-0.75); Colors[c++] = Vec4(0.0, 0.0, 1.0); Normals[n++] = Vec4( 0.0, 1.0, 0.0); Vertices[v++] = Vec4(-0.75, 0.0, 0.75);
	TexCoords[tc++] = Vec2( 0.75,-0.75); Colors[c++] = Vec4(0.0, 1.0, 0.0); Normals[n++] = Vec4( 0.0, 1.0, 0.0); Vertices[v++] = Vec4( 0.75, 0.0, 0.75);
	TexCoords[tc++] = Vec2( 0.75, 0.75); Colors[c++] = Vec4(0.0, 1.0, 1.0); Normals[n++] = Vec4( 0.0, 1.0, 0.0); Vertices[v++] = Vec4( 0.75, 0.0,-0.75);
	TexCoords[tc++] = Vec2( 0.75, 0.75); Colors[c++] = Vec4(0.0, 1.0, 1.0); Normals[n++] = Vec4( 0.0, 1.0, 0.0); Vertices[v++] = Vec4( 0.75, 0.0,-0.75);
	TexCoords[tc++] = Vec2(-0.75, 0.75); Colors[c++] = Vec4(1.0, 0.0, 0.0); Normals[n++] = Vec4( 0.0, 1.0, 0.0); Vertices[v++] = Vec4(-0.75, 0.0,-0.75);
	TexCoords[tc++] = Vec2(-0.75,-0.75); Colors[c++] = Vec4(0.0, 0.0, 1.0); Normals[n++] = Vec4( 0.0, 1.0, 0.0); Vertices[v++] = Vec4(-0.75, 0.0, 0.75);


	r->beginMesh("Test");
	for (int i = 0; i < 42; i++)
	{
		r->addVertex(Vertices[i], Colors[i], Normals[i], TexCoords[i]);
		r->addIndex(i);
	}
	Mesh* m = r->endMesh();

	Texture* tex = r->loadTexture("texture.jpg");
	m->material.texture = tex;

}


ScanLineRasterizerMT rasterizer;
bool button = false;
int dx, dy;
int ox, oy;
Vec3 Position, Reference;





void my_mouse_callback( int event, int x, int y, int flags, void* param ){
	if (event == CV_EVENT_LBUTTONDOWN){
		ox = x;
		oy = y;
		button = true;
	}
	else if (event == CV_EVENT_LBUTTONUP)
		button = false;
	if (event == CV_EVENT_MOUSEMOVE && button)
	{
		dx = ox - x;
		dy = oy - y;
		ox = x;
		oy = y;

		double sensitivity = 0.05;

		double hangle = (double)dx * sensitivity;
		double vangle = (double)dy * sensitivity;

		Quaternion qy, qz;
		qy.FromAngleAxis(Radian(Math::DegreesToRadians(hangle)), Vec3::UNIT_Y);
		qz.FromAngleAxis(Radian(Math::DegreesToRadians(vangle)), Vec3::UNIT_X);

		Matrix3 rot;
		(qy * qz).ToRotationMatrix(rot);

		Vec3 dir =  Reference - Position;
		dir.normalise();
		dir = rot * dir;
		Matrix4 view;
		view.lookAtLH(Position, Reference = Position + dir, Vec3(0,1,0));
		rasterizer.setTransform(IRasterizer::VIEW, view);
	}	
}

int main(int argc, char** argv){


	Vec4 a(10, 10, 10, 10);
	Vec4 b(1, 2, 3, 4);
	Vec4 c = a-b;
	Vec4 d = a + b;

	objLoader obj;
	int res = obj.load("H:\\Dropbox\\workspace\\source\\SoftwareRenderer\\statue2.obj");
	if (res != 1){
		std::cout << "Failed to load file" << std::endl;
		return 1;
	}

	int width = 800;
	int height = 600;
	{
		Mesh* m = rasterizer.addMesh("teapot", obj, Vec3(0,0,0), Vec3(0,0,0), Vec3(0.1f, -0.1f, 0.1f));
		Texture* tex = rasterizer.loadTexture("texture.jpg");
		m->material.texture = tex;
		//m->writeToFile("Foo.srm");

		//delete m;

		///*Mesh**/ m = new Mesh();
		//m->loadFromFile("Foo.srm");
		//Texture* tex2 = rasterizer.loadTexture("texture.jpg");
		//m->material.texture = tex2;
	}
	//buildMesh(&rasterizer);

	Matrix4 view;
	Position = Vec3(0, 0, -5);
	Reference = Vec3(0, 0, -150);
	view.lookAtLH(Position, Reference, Vec3(0,1,0));
	rasterizer.setTransform(IRasterizer::VIEW, view);

	Matrix4 proj;
	proj.setPerspective(45, width, height, 0.125, 128.0);
	//proj.setPerspectiveFovLH(Math::DegreesToRadians(45), 1.3333f, 0.125, 128.0);
	rasterizer.setTransform(IRasterizer::PROJECTION, proj);

	rasterizer.setVertexShader(SimpleVertexShader);
	rasterizer.setFragmentShader(SimpleFragmentShader);

	RenderTarget* rt = rasterizer.createRenderTarget(width, height);
	rasterizer.setRenderTarget(rt);

	double ms = rasterizer.render();
	
	cvShowImage("ColorBuffer", rt->colorBuffer);
	cvShowImage("DepthBuffer", rt->depthBuffer);

	cvSetMouseCallback("ColorBuffer", my_mouse_callback);

	double sum_ms = 0;
	int ren_co = 0;

	while(true){
		int k = cvWaitKey(1);
		bool updateView = false;
		if (k == 27)
			break;
		if (k == 119){
			std::cout << "w" << std::endl;
			Vec3 dir =  Reference - Position;
			dir.normalise();
			Position -= dir * 0.5;
			Reference -= dir * 0.5;
			updateView = true;
		}else if (k == 115){
			std::cout << "s" << std::endl;
			Vec3 dir =  Reference - Position;
			dir.normalise();
			Position += dir * 0.5;
			Reference += dir * 0.5;
			updateView = true;
		}else if (k == 97){
			std::cout << "a" << std::endl;
			Vec3 dir =  Reference - Position;
			dir.normalise();
			Vec3 right = dir.crossProduct(Vec3::UNIT_Y);
			Position -= right * 0.5;
			Reference -= right * 0.5;
			updateView = true;
		}else if (k == 100){
			std::cout << "d" << std::endl;
			Vec3 dir =  Reference - Position;
			dir.normalise();
			Vec3 right = dir.crossProduct(Vec3::UNIT_Y);
			Position += right * 0.5;
			Reference += right * 0.5;
			updateView = true;
		}
		if (updateView){
			Matrix4 view;
			view.lookAtLH(Position, Reference, Vec3(0,1,0));
			rasterizer.setTransform(IRasterizer::VIEW, view);
		}
		ms = rasterizer.render();
		sum_ms += ms;
		ren_co++;
		std::cout << ren_co << "- time to render: " << ms << "[ms] avg: " << (sum_ms / (double)ren_co) << "[ms]" << std::endl;
		cvShowImage("ColorBuffer", rt->colorBuffer);
		cvShowImage("DepthBuffer", rt->depthBuffer);
		if (ren_co == 1000){
			int a = 0;
			std::cout << "1000 runs" << std::endl;
			std::cin >> a;
		}
	}

	return 0;
}
